// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include <stdio.h>
#include <iostream>
#include <time.h>
#include "synch.h"
#include "processtable.h"
#include "bitmap.h"

#define MAX_LOCK 4000
#define MAX_CV 4000

using namespace std;

struct KernelLock {
	Lock *lock;
	AddrSpace *addrSpace;
	int inUse;
	bool toBeDelete;
};

struct KernelCondition {
	Condition *condition;
	AddrSpace *addrSpace;
	int inUse;
	bool toBeDelete;
};

Lock *lockMutex = new Lock("Lock Mutex");
Lock *conditionMutex = new Lock("CV Mutex");
int numberOfLocks = 0;
KernelLock *kernelLocks[MAX_LOCK];
BitMap freeLocks(MAX_LOCK);
BitMap freeCV(MAX_CV);
int numberOfConditions = 0;
KernelCondition *kernelConditions[MAX_CV];

int copyin(unsigned int vaddr, int len, char *buf) {
	// Copy len bytes from the current thread's virtual address vaddr.
	// Return the number of bytes so read, or -1 if an error occors.
	// Errors can generally mean a bad virtual address was passed in.
	bool result;
	int n=0;			// The number of bytes copied in
	int *paddr = new int;

	while ( n >= 0 && n < len) {
		result = machine->ReadMem( vaddr, 1, paddr );
		while(!result) // FALL 09 CHANGES
		{
			result = machine->ReadMem( vaddr, 1, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
		}	

		buf[n++] = *paddr;

		if ( !result ) {
	//translation failed
			return -1;
		}

		vaddr++;
	}

	delete paddr;
	return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
	// Copy len bytes to the current thread's virtual address vaddr.
	// Return the number of bytes so written, or -1 if an error
	// occors.  Errors can generally mean a bad virtual address was
	// passed in.
	bool result;
	int n=0;			// The number of bytes copied in

	while ( n >= 0 && n < len) {
	// Note that we check every byte's address
		result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );

		if ( !result ) {
	//translation failed
			return -1;
		}

		vaddr++;
	}

	return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
	// Create the file with the name in the user buffer pointed to by
	// vaddr.  The file name is at most MAXFILENAME chars long.  No
	// way to return errors, though...
	char *buf = new char[len+1];	// Kernel buffer to put the name in

	if (!buf) return;

	if( copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Create\n");
		delete buf;
		return;
	}

	buf[len]='\0';

	fileSystem->Create(buf,0);
	delete[] buf;
	return;
}

int Open_Syscall(unsigned int vaddr, int len) {
	// Open the file with the name in the user buffer pointed to by
	// vaddr.  The file name is at most MAXFILENAME chars long.  If
	// the file is opened successfully, it is put in the address
	// space's file table and an id returned that can find the file
	// later.  If there are any errors, -1 is returned.
	char *buf = new char[len+1];	// Kernel buffer to put the name in
	OpenFile *f;			// The new open file
	int id;				// The openfile id

	if (!buf) {
		printf("%s","Can't allocate kernel buffer in Open\n");
		return -1;
	}

	if( copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Open\n");
		delete[] buf;
		return -1;
	}

	buf[len]='\0';

	f = fileSystem->Open(buf);
	delete[] buf;

	if ( f ) {
		if ((id = currentThread->space->fileTable.Put(f)) == -1 )
			delete f;
		return id;
	}
	else
		return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
	// Write the buffer to the given disk file.  If ConsoleOutput is
	// the fileID, data goes to the synchronized console instead.  If
	// a Write arrives for the synchronized Console, and no such
	// console exists, create one. For disk files, the file is looked
	// up in the current address space's open file table and used as
	// the target of the write.

	char *buf;		// Kernel buffer for output
	OpenFile *f;	// Open file for output

	if ( id == ConsoleInput) return;

	if ( !(buf = new char[len]) ) {
		printf("%s","Error allocating kernel buffer for write!\n");
		return;
	} else {
		if ( copyin(vaddr,len,buf) == -1 ) {
			printf("%s","Bad pointer passed to to write: data not written\n");
			delete[] buf;
			return;
		}
	}

	if ( id == ConsoleOutput) {
		for (int ii=0; ii<len; ii++) {
			printf("%c",buf[ii]);
		}

	} else {
		if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
			f->Write(buf, len);
		} else {
			printf("%s","Bad OpenFileId passed to Write\n");
			len = -1;
		}
	}

	delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
	// Write the buffer to the given disk file.  If ConsoleOutput is
	// the fileID, data goes to the synchronized console instead.  If
	// a Write arrives for the synchronized Console, and no such
	// console exists, create one.    We reuse len as the number of bytes
	// read, which is an unnessecary savings of space.
	char *buf;		// Kernel buffer for input
	OpenFile *f;	// Open file for output

	if ( id == ConsoleOutput) return -1;

	if ( !(buf = new char[len]) ) {
		printf("%s","Error allocating kernel buffer in Read\n");
		return -1;
	}

	if ( id == ConsoleInput) {
	//Reading from the keyboard
		scanf("%s", buf);

		if ( copyout(vaddr, len, buf) == -1 ) {
			printf("%s","Bad pointer passed to Read: data not copied\n");
		}
	} else {
		if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
			len = f->Read(buf, len);
			if ( len > 0 ) {
			//Read something from the file. Put into user's address space
				if ( copyout(vaddr, len, buf) == -1 ) {
					printf("%s","Bad pointer passed to Read: data not copied\n");
				}
			}
		} else {
			printf("%s","Bad OpenFileId passed to Read\n");
			len = -1;
		}
	}

	delete[] buf;
	return len;
}

void Kernel_Thread(int addr) {
	// Kernel Thread
	// Alloc 8 pages of stack to a new thread in a process
	DEBUG('b', "Running New Thread.\n");
	AddrSpace* space = currentThread->space;
	
	// get new pages
	int saddr = space->NewStack();
	
	// init registers
	for (int i = 0; i < NumTotalRegs; i++)
		machine->WriteRegister(i, 0);
	DEBUG('A', "PCReg %d.\n", addr);
	DEBUG('A', "NextPCReg %d.\n", addr+4);
	DEBUG('A', "StackReg %d.\n", saddr-16);
	machine->WriteRegister(PCReg, addr);
	machine->WriteRegister(NextPCReg, addr+4);
	machine->WriteRegister(StackReg, saddr - 16);
	
	// run the thread
	machine->Run();
}

// handle Fork syscall
void Fork_Syscall(unsigned int taddr) {
	// fork sysetm call
	// fork a kernel thread to run a new thread in a process
	
	DEBUG('f', "Fork %d.\n", taddr);
	Thread* t = new Thread("User Thread");
	if (currentThread->startAddr == taddr) {
		// check if fork itself
		// do not allow
		// just quit fork
		printf("Cannot fork oneself.\n");
		return;
	}
	
	// same addrspace
	t->space = currentThread->space;
	t->startAddr = taddr;
	processTableLock->Acquire();
	// add a new live count before everything
	liveThreadCount++;
	t->threadID = processID++;
	t->space->liveThreadCount++;
	processTableLock->Release();
	
	// fork the kernal thread
	t->Fork(Kernel_Thread, taddr);
}

// a new process
void Kernel_Process(int data) {
	// a process
	DEBUG('b', "Running New Process.\n");
	OpenFile* executable = (OpenFile*)data;
	// alloc memory for the process
	AddrSpace *space = new AddrSpace(executable);
	currentThread->space = space;
	// delete executable;
	space->InitRegisters();
	space->RestoreState();
	// run
	machine->Run();
}

// handle Exec syscall
int Exec_Syscall(unsigned int openfileaddr, int len) {
	char *buf = new char[len+1];	// Kernel buffer to put the name in

	if (!buf) return -1;

	if( copyin(openfileaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Exec\n");
		delete[] buf;
		return -1;
	}
	buf[len]='\0';
	DEBUG('z',"Hello World.\n");
	OpenFile *executable = fileSystem->Open(buf);
    if (executable == NULL) {
		// file not exsits
		// exit
		printf("Unable to open file %s\n", buf);
		delete[] buf;
		return -1;
    } else {
		Thread* kernelProcess = new Thread(buf);
		delete[] buf;
		// live count
		processTableLock->Acquire();
		liveThreadCount++;
		kernelProcess->threadID = processID++;
		processTableLock->Release();
		
		// fork the kernal process
		kernelProcess->Fork(Kernel_Process, (int)executable);
		return 0;
	}
}

// handle Exit syscall
void Exit_Syscall(int status) {
	AddrSpace *space = currentThread->space;
	if (status)
		printf("The thread exit with signal %d.\n", status);
	processTableLock->Acquire();
	// a thread leaves
	liveThreadCount--;
	space->liveThreadCount--;
	if (liveThreadCount == 0) {
		processTableLock->Release();
		// last process
		// just halt the machine
		delete space;
		DEBUG('1',"Last Process.\n");
		interrupt->Halt();
	} else if (space->liveThreadCount != 0) {
		// not last thread
		processTableLock->Release();
		DEBUG('1', "Not Last Thread.\n");
		// deallocate stack space
		space->DeleteStack();
		currentThread->Finish();
	} else {
		processTableLock->Release();
		// last thread in a process.
		// dealloc the memory, locks and conditions.
		DEBUG('1', "Last Thread in a Process.\n");
		// deallocate locks and conditions
		lockMutex->Acquire();
		for(int i = 0; i < MAX_LOCK; i++) {
			if (freeLocks.Test(i) && kernelLocks[i]->addrSpace == currentThread->space) {
				freeLocks.Clear(i);
				delete kernelLocks[i]->lock;
				kernelLocks[i]->lock = NULL;
			}
		}
		lockMutex->Release();
		conditionMutex->Acquire();
		for(int i = 0; i < MAX_CV; i++) {
			if (freeCV.Test(i) && kernelConditions[i]->addrSpace == currentThread->space) {
				freeCV.Clear(i);
				delete kernelConditions[i]->condition;
				kernelConditions[i]->condition = NULL;
			}
		}
		conditionMutex->Release();
		// deallocate memory
		delete space;
		currentThread->Finish();	
	}
}

#ifndef NETWORK

// handle CreateLock syscall
int CreateLock_Syscall(unsigned int vaddr, int len) {
	// create a lock
	char *buf = new char[len+1];	// Kernel buffer to put the name in

	if (!buf) return -1;

	if( copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Create\n");
		delete[] buf;
		return -1;
	}
	buf[len]='\0';
	lockMutex->Acquire();
	int rv = freeLocks.Find();
	if (rv < 0) {
		// no locks
		printf("Max Lock Exceeded.\n");
		return -1;
	}
	
	// create a lock
	kernelLocks[rv] = new KernelLock;
	kernelLocks[rv]->lock = new Lock(buf);
	kernelLocks[rv]->addrSpace = currentThread->space;
	kernelLocks[rv]->inUse = 0;
	kernelLocks[rv]->toBeDelete = false;
	lockMutex->Release();
	delete[] buf;
	return rv;	 
}

// handle Acquire Syscall
void AcquireLock_Syscall(int lockId) {
	DEBUG('L',"Acquire Lock.\n");
	lockMutex->Acquire();
	if (lockId < 0 || lockId >= MAX_LOCK || kernelLocks[lockId] == NULL 
		|| kernelLocks[lockId]->lock == NULL) {
		// lock's deleted
		printf("%s","Lock is already deleted.\n");
		lockMutex->Release();
	} else if (kernelLocks[lockId]->addrSpace != currentThread->space) {
		// lock's not belong to the process
		printf("%s","Lock is not belong to this process.\n");
		lockMutex->Release();
	} else {
		// acquire the lock
		kernelLocks[lockId]->inUse++;
		lockMutex->Release();
		kernelLocks[lockId]->lock->Acquire();
	}
} 

void ReleaseLock_Syscall(int lockId) {
	DEBUG('L',"Release Lock.\n");
	lockMutex->Acquire();
	if (lockId < 0 || lockId >= MAX_LOCK || kernelLocks[lockId] == NULL 
		|| kernelLocks[lockId]->lock == NULL) {
		// lock's deleted
		printf("%s","Lock is already deleted.\n");
		lockMutex->Release();
	} else if (kernelLocks[lockId]->addrSpace != currentThread->space) {
		// lock's not belong to the process
		printf("%s","Lock is not belong to this process.\n");
		lockMutex->Release();
	} else {
		// release the lock
		kernelLocks[lockId]->inUse--;
		kernelLocks[lockId]->lock->Release();
		// check the lock is to be deleted.
		if (kernelLocks[lockId]->inUse == 0 && kernelLocks[lockId]->toBeDelete) {
			// delete the lock
			DEBUG('L',"Delete Lock.\n");
			delete kernelLocks[lockId]->lock;
			freeLocks.Clear(lockId);
			kernelLocks[lockId]->lock = NULL;
		}
		lockMutex->Release();
	}
}

void DeleteLock_Syscall(int lockId) {
	lockMutex->Acquire();
	if (lockId < 0 || lockId >= MAX_LOCK || kernelLocks[lockId] == NULL 
		|| kernelLocks[lockId]->lock == NULL) {
		printf("%s","Lock is already deleted.\n");
		lockMutex->Release();
	} else if (kernelLocks[lockId]->addrSpace != currentThread->space) {
		printf("%s","Lock is not belong to this process.\n");
		lockMutex->Release();
	} else {
		// set the lock to be deleted
		// delete the lock when free
		kernelLocks[lockId]->toBeDelete = true;
		if (kernelLocks[lockId]->inUse == 0) {
			DEBUG('L',"Delete Lock.\n");
			delete kernelLocks[lockId]->lock;
			freeLocks.Clear(lockId);
			kernelLocks[lockId]->lock = NULL;
		}
		lockMutex->Release();
	}
}

int CreateCondition_Syscall(unsigned int vaddr, int len) {
	char *buf = new char[len+1];	// Kernel buffer to put the name in

	if (!buf) return -1;

	if( copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Create\n");
		delete[] buf;
		return -1;
	}
	buf[len]='\0';
	conditionMutex->Acquire();
	int rv = freeCV.Find();
	kernelConditions[rv] = new KernelCondition;
	kernelConditions[rv]->condition = new Condition(buf);
	kernelConditions[rv]->addrSpace = currentThread->space;
	kernelConditions[rv]->inUse = 0;
	kernelConditions[rv]->toBeDelete = false;
	conditionMutex->Release();
	delete[] buf;
	DEBUG('T', "Create Condition %d.\n", rv);
	return rv;	
}

// handle Wait Syscall
// if the last thread, then exit
void ConditionWait_Syscall(int conditionId, int lockId) {
	lockMutex->Acquire();
	if (lockId < 0 || lockId >= MAX_LOCK || kernelLocks[lockId] == NULL 
		|| kernelLocks[lockId]->lock == NULL) {
		printf("%s","Lock is already deleted.\n");
		lockMutex->Release();
	} else if (kernelLocks[lockId]->addrSpace != currentThread->space) {
		printf("%s","Lock is not belong to this process.\n");
		lockMutex->Release();
	} else if (!kernelLocks[lockId]->lock->isHeldByCurrentThread()){
		printf("%s","Current thread does not have the lock.\n");
		lockMutex->Release();
	} else {
		lockMutex->Release();
		conditionMutex->Acquire();
		if (conditionId < 0 || conditionId >= MAX_CV || 
			kernelConditions[conditionId] == NULL || 
				kernelConditions[conditionId]->condition == NULL) {
			printf("Condition %d is already deleted...\n", conditionId);
			conditionMutex->Release();
		} else if (kernelConditions[conditionId]->addrSpace != currentThread->space) {
			printf("%s","Condition is not belong to this process.\n");
			conditionMutex->Release();
		} else {
			// condition and lock is valid.
			// inactive threads
			DEBUG('T', "Sleep.\n");			
			conditionMutex->Release();
			processTableLock->Acquire();
			liveThreadCount--;
			kernelConditions[conditionId]->inUse++;
			currentThread->space->liveThreadCount--;
			// exit part
			if (liveThreadCount == 0) {
				processTableLock->Release();
				delete currentThread->space;
				DEBUG('1',"Last Process......\n");
				interrupt->Halt();
			} else if (currentThread->space->liveThreadCount == 0) {
				processTableLock->Release();
				DEBUG('1', "Last Thread in a Process.\n");
				// deallocate locks and conditions
				lockMutex->Acquire();
				for(int i = 0; i < MAX_LOCK; i++) {
					if (freeLocks.Test(i) && kernelLocks[i]->addrSpace == currentThread->space) {
						freeLocks.Clear(i);
						delete kernelLocks[i]->lock;
						kernelLocks[i]->lock = NULL;
					}
				}
				lockMutex->Release();
				conditionMutex->Acquire();
				for(int i = 0; i < MAX_CV; i++) {
					if (freeCV.Test(i) && kernelConditions[i]->addrSpace == currentThread->space) {
						freeCV.Clear(i);
						DEBUG('T', "***DELETE Condition %d.\n", i);
						delete kernelConditions[i]->condition;
						kernelConditions[i]->condition = NULL;
					}
				}
				conditionMutex->Release();
				// deallocate memory
				delete currentThread->space;
				currentThread->Finish();
			}
			processTableLock->Release();
			kernelConditions[conditionId]->condition->Wait(
				kernelLocks[lockId]->lock);
		}
	}
}

void DeleteCondition_Syscall(int conditionId) {
	//delete the condition
	conditionMutex->Acquire();
	if (conditionId < 0 || conditionId >= MAX_CV || 
		kernelConditions[conditionId] == NULL || 
			kernelConditions[conditionId]->condition == NULL) {
		printf("Condition %d is already deleted.\n", conditionId);
		conditionMutex->Release();
	} else if (kernelConditions[conditionId]->addrSpace != currentThread->space) {
		printf("%s","Condition is not belong to this process.\n");
		conditionMutex->Release();
	} else {
		// set to be delete
		// when no one uses, delete
		kernelConditions[conditionId]->toBeDelete = true;
		if (kernelConditions[conditionId]->inUse == 0) {
			delete kernelConditions[conditionId]->condition;
			freeCV.Clear(conditionId);
			kernelConditions[conditionId]->condition = NULL;
		}
		conditionMutex->Release();
	}
}

// handles Signal Syscall
// check if the condition is to be deleted
void ConditionSignal_Syscall(int conditionId, int lockId) {
	lockMutex->Acquire();
	// check the lock is valid?
	if (lockId < 0 || lockId >= MAX_LOCK || kernelLocks[lockId] == NULL 
		|| kernelLocks[lockId]->lock == NULL) {
		printf("%s","Lock is already deleted.\n");
		lockMutex->Release();
	} else if (kernelLocks[lockId]->addrSpace != currentThread->space) {
		printf("%s","Lock is not belong to this process.\n");
		lockMutex->Release();
	} else if (!kernelLocks[lockId]->lock->isHeldByCurrentThread()){
		printf("%s","Current thread does not have the lock.\n");
		lockMutex->Release();
	} else {
		// the lock is valid
		lockMutex->Release();
		conditionMutex->Acquire();
		if (conditionId < 0 || conditionId >= MAX_CV || 
			kernelConditions[conditionId] == NULL || 
				kernelConditions[conditionId]->condition == NULL) {
			printf("%s","Condition is already deleted.\n");
			conditionMutex->Release();
		} else if (kernelConditions[conditionId]->addrSpace != currentThread->space) {
			printf("%s","Condition is not belong to this process.\n");
			conditionMutex->Release();
		} else {
			// condition is valid
			// if wake up someone
			int t = kernelConditions[conditionId]->condition->Signal(
				kernelLocks[lockId]->lock);
			DEBUG('T',"Wake up %d.\n", t);
			processTableLock->Acquire();
			// active thread
			liveThreadCount+=t;
			currentThread->space->liveThreadCount+=t;
			kernelConditions[conditionId]->inUse -= t;
			processTableLock->Release();
			if (kernelConditions[conditionId]->inUse == 0 && kernelConditions[conditionId]->toBeDelete) {
				// check the condition is to be deleted
				delete kernelConditions[conditionId]->condition;
				freeCV.Clear(conditionId);
				kernelConditions[conditionId]->condition = NULL;
			}
			conditionMutex->Release();
		}
	}
}

// handles Broadcast Syscall
// check if the condition is to be deleted
void ConditionBroadcast_Syscall(int conditionId, int lockId) {
	lockMutex->Acquire();
	// check lock is valid?
	if (lockId < 0 || lockId >= MAX_LOCK || kernelLocks[lockId] == NULL 
		|| kernelLocks[lockId]->lock == NULL) {
		printf("%s","Lock is already deleted.\n");
		lockMutex->Release();
	} else if (kernelLocks[lockId]->addrSpace != currentThread->space) {
		printf("%s","Lock is not belong to this process.\n");
		lockMutex->Release();
	} else if (!kernelLocks[lockId]->lock->isHeldByCurrentThread()){
		printf("%s","Current thread does not have the lock.\n");
		lockMutex->Release();
	} else {
		// lock is valid
		lockMutex->Release();
		conditionMutex->Acquire();
		// check condition is valid?
		if (conditionId < 0 || conditionId >= MAX_CV || 
			kernelConditions[conditionId] == NULL || 
				kernelConditions[conditionId]->condition == NULL) {
			printf("%s","Condition is already deleted.\n");
			conditionMutex->Release();
		} else if (kernelConditions[conditionId]->addrSpace != currentThread->space) {
			printf("%s","Condition is not belong to this process.\n");
			conditionMutex->Release();
		} else {
			// condition is valid
			// broadcast
			int t = kernelConditions[conditionId]->condition->Broadcast(
				kernelLocks[lockId]->lock);
			DEBUG('T', "Wake up %d.\n", t);
			processTableLock->Acquire();
			liveThreadCount+=t;
			// active thread
			currentThread->space->liveThreadCount+=t;
			kernelConditions[conditionId]->inUse -= t;
			processTableLock->Release();
			if (kernelConditions[conditionId]->inUse == 0 && kernelConditions[conditionId]->toBeDelete) {
				// check if condition is to be deleted
				delete kernelConditions[conditionId]->condition;
				freeCV.Clear(conditionId);
				kernelConditions[conditionId]->condition = NULL;
			}
			conditionMutex->Release();
		}
	}
}
#else

#define SERVER (rand()%numberOfServers)
#define SERVER_MB 0

#include "post.h"

int atoi_(char *m, int size) {
	int ret = 0;
	DEBUG('I', "%d %c\n",size, *m);
	for(int i = 0; i < size; i++, m++) {
		ret = ret * 10 + (*m) - '0';
	}
	return ret;
}

int atoi(char *m) {
	int ret = 0;
	for(;*m;m++) {
		ret = ret * 10 + (*m) - '0';
	}
	return ret;
}

int itoa_(int x, char *m) {
	char buffer[MaxMailSize];
	int count = 0;
	do {
		buffer[count++] = x % 10;
		x /= 10;
	} while(x);
	for(int i = 0; i < count; i++) {
		m[i] = buffer[count-1-i]+'0';
	}
	return count;
}

// handle CreateLock syscall
int CreateLock_Syscall(unsigned int vaddr, int len) {
	// create a lock
	char *buf = new char[len+3+20];	// Kernel buffer to put the name in
	
	if (len > 50) {
		printf("%s", "Lock name too long\n");
		interrupt->Halt();
	}
	
	if( copyin(vaddr,len,buf+2) == -1 ) {
		printf("%s","Bad pointer passed to Create\n");
		delete[] buf;
		interrupt->Halt();
	}
	buf[len+2]='\0';
	buf[0] = 11;
	buf[1] = len;
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	time_t t = time(NULL);
	int tt = (int)t;
	int len1 = itoa_(tt, buf+len+3);
	buf[len+2] = len1;

	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = len + 3 + len1;
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }

	char buffer[MaxMailSize];
	
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	if (*buffer == 0) {
		printf("Create lock failed.\n");
		interrupt->Halt();
	}
	int rv = atoi(buffer);
	
	return rv;	 
}

// handle Acquire Syscall
void AcquireLock_Syscall(int lockId) {
	DEBUG('N',"Acquire....\n");
	char message[4];
	message[0] = 13;
	message[1] = lockId / 100;
	message[2] = lockId % 100;
	message[3] = 0;
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = 4;
	bool success = postOffice->Send(outPktHdr, outMailHdr, message);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	char buffer[MaxMailSize];
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	
	if (buffer[0] != 0) {
		printf("Lock is not belong to this machine. Halting...\n");
		interrupt->Halt();
	}
} 

void ReleaseLock_Syscall(int lockId) {
	DEBUG('N',"Release....\n");
	char message[4];
	message[0] = 14;
	message[1] = lockId / 100;
	message[2] = lockId % 100;
	message[3] = 0;
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = 4;
	bool success = postOffice->Send(outPktHdr, outMailHdr, message);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	char buffer[MaxMailSize];
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	
	if (buffer[0] == 1) {
		printf("Lock is not belong to this machine. Halting..\n");
		interrupt->Halt();
	}
}

void DeleteLock_Syscall(int lockId) {
	DEBUG('N',"Release....\n");
	char message[4];
	message[0] = 12;
	message[1] = lockId / 100;
	message[2] = lockId % 100;
	message[3] = 0;
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = 4;
	bool success = postOffice->Send(outPktHdr, outMailHdr, message);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	char buffer[MaxMailSize] = {0};
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	
	if (buffer[0] != 0) {
		DEBUG('l', "%d\n", buffer[0]);
		printf("Lock is not belong to this machine. Halting.....\n");
		interrupt->Halt();
	}
}

int CreateCondition_Syscall(unsigned int vaddr, int len) {
	char *buf = new char[len+3+20];	// Kernel buffer to put the name in
	
	if (len > 50) {
		printf("%s", "CV name too long\n");
		interrupt->Halt();
	}
	
	if( copyin(vaddr,len,buf+2) == -1 ) {
		printf("%s","Bad pointer passed to Create\n");
		delete[] buf;
		interrupt->Halt();
	}
	buf[len+2]='\0';
	buf[0] = 15;
	buf[1] = len;
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	time_t t = time(NULL);
	int tt = (int)t;
	int len1 = itoa_(tt, buf+len+3);
	buf[len+2] = len1;
	
	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = len + 3 + len1;
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }

	char buffer[MaxMailSize];
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	if (*buffer == 0) {
		printf("Create lock failed.\n");
		interrupt->Halt();
	}
	int rv = atoi(buffer);
	
	return rv;	 
}

// handle Wait Syscall
// if the last thread, then exit
void ConditionWait_Syscall(int conditionId, int lockId) {
	char message[6];
	message[0] = 17;
	message[1] = conditionId / 100;
	message[2] = conditionId % 100;
	message[3] = lockId / 100;
	message[4] = lockId % 100;
	message[5] = 0;
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = 6;
	bool success = postOffice->Send(outPktHdr, outMailHdr, message);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	char buffer[MaxMailSize];
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	
	if (buffer[0] != 0) {
		DEBUG('l', "%d\n", buffer[0]);
		printf("CV or lock is not belong to this machine. Halting...\n");
		interrupt->Halt();
	} 
}

void DeleteCondition_Syscall(int conditionId) {
	//delete the condition
	char message[4];
	message[0] = 16;
	message[1] = conditionId / 100;
	message[2] = conditionId % 100;
	message[3] = 0;
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = 4;
	bool success = postOffice->Send(outPktHdr, outMailHdr, message);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	char buffer[MaxMailSize];
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	
	if (buffer[0] != 0) {
		DEBUG('l', "%d\n", buffer[0]);
		printf("Condition is not belong to this machine. Halting...\n");
		interrupt->Halt();
	}
}

// handles Signal Syscall
// check if the condition is to be deleted
void ConditionSignal_Syscall(int conditionId, int lockId) {
	char message[6];
	message[0] = 18;
	message[1] = conditionId / 100;
	message[2] = conditionId % 100;
	message[3] = lockId / 100;
	message[4] = lockId % 100;
	message[5] = 0;
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = 6;
	bool success = postOffice->Send(outPktHdr, outMailHdr, message);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	char buffer[MaxMailSize];
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	
	if (buffer[0] != 0) {
		printf("CV or lock is not belong to this machine. Halting...\n");
		interrupt->Halt();
	} 
}

// handles Broadcast Syscall
// check if the condition is to be deleted
void ConditionBroadcast_Syscall(int conditionId, int lockId) {
	// init the message
	char message[6];
	message[0] = 19;
	message[1] = conditionId / 100;
	message[2] = conditionId % 100;
	message[3] = lockId / 100;
	message[4] = lockId % 100;
	message[5] = 0;
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = 6;
	bool success = postOffice->Send(outPktHdr, outMailHdr, message);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	char buffer[MaxMailSize];
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	
	if (buffer[0] != 0) {
		printf("CV or lock is not belong to this machine. Halting...\n");
		interrupt->Halt();
	} 
}

int CreateMonitor_Syscall(unsigned int vaddr, int len, int size) {
	// init the message
	char *buf = new char[len+3+20];	// Kernel buffer to put the name in
	
	if (len > 50 || size < 0) {
		printf("MV name too long %d %d.\n", len, currentThread->threadID);
		copyin(vaddr,len,buf);
		buf[len] = 0;
		printf("%s.\n", buf);
		interrupt->Halt();
	}
	
	if( copyin(vaddr,len,buf+4) == -1 ) {
		printf("%s","Bad pointer passed to Create\n");
		delete[] buf;
		interrupt->Halt();
	}
	buf[len+4]='\0';
	buf[0] = 21;
	buf[1] = size / 100;
	buf[2] = size % 100;
	buf[3] = len;
	DEBUG('N', "Hello\n");
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	time_t t = time(NULL);
	int tt = (int)t;
	int len1 = itoa_(tt, buf+len+5);
	buf[len+4] = len1;
	
	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = len + 5 + len1;

	bool success = postOffice->Send(outPktHdr, outMailHdr, buf);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }

	char buffer[MaxMailSize];
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	if (buffer[0] == 0) {
		printf("Create MV failed. Halting.\n");
		interrupt->Halt();
	}
	int rv = atoi(buffer);
	
	return rv;	 
	
}

void DestoryMonitor_Syscall(int mvid) {
	// init the message
	char message[4];
	message[0] = 22;
	message[1] = mvid / 100;
	message[2] = mvid % 100;
	message[3] = 0;
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = 4;
	bool success = postOffice->Send(outPktHdr, outMailHdr, message);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	char buffer[MaxMailSize];
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	
	if (buffer[0] != 0) {
		printf("MV is not belong to this machine. Halting...\n");
		interrupt->Halt();
	}
}

int ReadMonitor_Syscall(int mvid, int index) {
	// init the message
	char message[6];
	message[0] = 23;
	message[1] = mvid / 100;
	message[2] = mvid % 100;
	message[3] = index / 100;
	message[4] = index % 100;
	message[5] = 0;
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = 6;

	// send the message
	bool success = postOffice->Send(outPktHdr, outMailHdr, message);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }

	// recieve the message
	char buffer[MaxMailSize];
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	
	if (buffer[0] == 0) {
		printf("MV Read Error. Halting...\n");
		interrupt->Halt();
	}
	
	int rv = atoi_(buffer+1, (int)buffer[0]);
	DEBUG('R',"%d\n", rv);
	return rv;
}

void SetMonitor_Syscall(int mvid, int index, int value) {
	// init the message
	char message[MaxMailSize];
	message[0] = 24;
	message[1] = mvid / 100;
	message[2] = mvid % 100;
	message[3] = index / 100;
	message[4] = index % 100;
	message[5] = itoa_(value, message+6);
	
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;

	outPktHdr.to = SERVER;		
    outMailHdr.to = SERVER_MB;
    outMailHdr.from = currentThread->threadID;
    outMailHdr.length = message[5] + 6;
	// send the message
	bool success = postOffice->Send(outPktHdr, outMailHdr, message);
	if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	char buffer[MaxMailSize];
	// recieve the message
	postOffice->Receive(currentThread->threadID, &inPktHdr, &inMailHdr, buffer);
	
	
	if (buffer[0] != 0) {
		printf("MV Set Error. Halting...\n");
		interrupt->Halt();
	}
}


#endif

void Close_Syscall(int fd) {
	// Close the file associated with id fd.  No error reporting.
	OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);

	if ( f ) {
		delete f;
	} else {
		printf("%s","Tried to close an unopen file\n");
	}
}
/*
int handleMemoryFull() {
	int ppn = rand() % NumPhysPages;
	if (isFifo) {
		DEBUG('p', "FIFO\n");
		// fifo algorithm
		ppnQueueLock->Acquire();
		ppn = (int)ppnQueue->Remove() - MAGIC_NUMBER;
		ppnQueueLock->Release();
		iptMutex->P();
		while (ipt[ppn].use || ipt[ppn].change) {
			ppnQueueLock->Acquire();
			iptMutex->V();
			ppnQueue->Append((void *)(ppn+MAGIC_NUMBER));
			ppn = (int)ppnQueue->Remove() - MAGIC_NUMBER;
			ppnQueueLock->Release();
			iptMutex->P();
		}
		ipt[ppn].change = TRUE;
		pageTableLock->Acquire();
		iptMutex->V();
	} else {
		iptMutex->P();
		while (ipt[ppn].use||ipt[ppn].change) {
			iptMutex->V();
			ppn = rand() % NumPhysPages;
			iptMutex->P();
		}
		ipt[ppn].change = TRUE;
		pageTableLock->Acquire();
		iptMutex->V();
	}
	
	DEBUG('#', "Physical Page %d Selected\n", ppn);
	
	IntStatus oldLevel = interrupt->SetLevel(IntOff);
	// check the page to replace is in 
	for(int i = 0; i < TLBSize; i++) {
		if (machine->tlb[i].valid && machine->tlb[i].physicalPage == ppn) {
			machine->tlb[i].valid = false;
			ipt[ppn].dirty = machine->tlb[i].dirty;
			break;
		}
	}
	interrupt->SetLevel(oldLevel);
	
	if (ipt[ppn].valid && ipt[ppn].dirty) {
		DEBUG('v', "Write File\n");
		// write on swap file
		int vpn = ipt[ppn].virtualPage;
		if (ipt[ppn].process->pageTable[vpn].location != SWAP) {
			// alloc a new space in swap file
			ipt[ppn].process->pageTable[vpn].location = SWAP;
			int offset = swapBitMap->Find();
			DEBUG('r', "OFFSET %d\n", offset);
			ipt[ppn].process->pageTable[vpn].offset = offset * PageSize;
		}
		int offset = ipt[ppn].process->pageTable[vpn].offset;
		DEBUG('r', "OFFSET+ %d\n", offset);
		swapFile->WriteAt(&(machine->mainMemory[ppn*PageSize]), PageSize, offset);
	}
	int vpn = ipt[ppn].virtualPage;
	ipt[ppn].process->pageTable[vpn].physicalPage = -1;
	ipt[ppn].process->pageTable[vpn].valid = FALSE;
	
	pageTableLock->Release();
	
	return ppn;
}

int handleIPTMiss(int vpn) {
	DEBUG('D', "Memory ENTER.\n");
	memoryLock->Acquire();
	int ppn = phyMem->Find();
	if (ppn != -1) {
		ipt[ppn].change = true;
	}
	memoryLock->Release();
	
	if (ppn == -1) {
		// when memory is full
		printf("Memory Full.\n");
		interrupt->Halt();
		ppn = handleMemoryFull();
	}
	DEBUG('D', "Memory MID.\n");
	
	DEBUG('8', "PPN %d VPN %d\n", ppn, vpn);
	if (isFifo) {
		ppnQueueLock->Acquire();
		ppnQueue->Append((void *)(ppn + MAGIC_NUMBER));
		ppnQueueLock->Release();
	}
	
	// load page into memory
	pageTableLock->Acquire();
	DEBUG('D', "Memory MID2.\n");
	if (currentThread->space->pageTable[vpn].location == EXECUTABLE) {
		DEBUG('v', "PPN- %d\n", ppn);
		DEBUG('V', "%d %d %d\n", ppn, vpn, currentThread->space->pageTable[vpn].offset);
		
		currentThread->space->_executable->ReadAt(&(machine->mainMemory[ppn*PageSize]),PageSize,
			currentThread->space->pageTable[vpn].offset);			
	} else if (currentThread->space->pageTable[vpn].location == SWAP) {
		DEBUG('v', "PPN+ %d\n", ppn);
		DEBUG('V', "%d %d %d\n", ppn, vpn, currentThread->space->pageTable[vpn].offset);
		swapFile->ReadAt(&(machine->mainMemory[ppn*PageSize]),PageSize,
			currentThread->space->pageTable[vpn].offset);
	}
	currentThread->space->pageTable[vpn].valid = TRUE;
	currentThread->space->pageTable[vpn].physicalPage = ppn;
	pageTableLock->Release();
	
	// set ipt block
	ipt[ppn].virtualPage = vpn;
	ipt[ppn].physicalPage = ppn;
	ipt[ppn].valid = TRUE;
	ipt[ppn].readOnly = currentThread->space->pageTable[vpn].readOnly;
	ipt[ppn].dirty = FALSE;
	// ipt[ppn].use = FALSE;
	ipt[ppn].process = currentThread->space;
	DEBUG('v', "Exit\n");
	DEBUG('D', "Memory Full EXIT.\n");
	return ppn;
}
*/

void ExceptionHandler(ExceptionType which) {
	int type = machine->ReadRegister(2); // Which syscall?
	int rv=0; 	// the return value from a syscall

	if ( which == SyscallException ) {
		switch (type) {
			default:
			DEBUG('c', "Unknown syscall - shutting down.\n");
			case SC_Halt:
			DEBUG('c', "Shutdown, initiated by user program.\n");
			interrupt->Halt();
			break;
			case SC_Create:
			DEBUG('c', "Create syscall.\n");
			Create_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_Open:
			DEBUG('c', "Open syscall.\n");
			rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_Write:
			DEBUG('c', "Write syscall.\n");
			Write_Syscall(machine->ReadRegister(4),
				machine->ReadRegister(5),
				machine->ReadRegister(6));
			break;
			case SC_Read:
			DEBUG('c', "Read syscall.\n");
			rv = Read_Syscall(machine->ReadRegister(4),
				machine->ReadRegister(5),
				machine->ReadRegister(6));
			break;
			case SC_Close:
			DEBUG('c', "Close syscall.\n");
			Close_Syscall(machine->ReadRegister(4));
			break;
			case SC_Yield:
			DEBUG('c', "Yield syscall.\n");
			currentThread->Yield();
			break;
			case SC_Fork:
			DEBUG('c', "Fork syscall.\n");
			Fork_Syscall(machine->ReadRegister(4));
			break;
			case SC_Exec:
			DEBUG('c', "Exec syscall.\n");
			rv = Exec_Syscall(machine->ReadRegister(4), 
				machine->ReadRegister(5));
			break;
			case SC_Exit:
			DEBUG('c', "Exit syscall.\n");
			Exit_Syscall(machine->ReadRegister(4));
			break;
			case SC_CreateLock:
			DEBUG('c', "CreateLock syscall.\n");
			rv = CreateLock_Syscall(machine->ReadRegister(4),
				machine->ReadRegister(5));
			break;
			case SC_Acquire:
			DEBUG('c', "AcquireLock syscall.\n");
			AcquireLock_Syscall(machine->ReadRegister(4));
			break;
			case SC_Release:
			DEBUG('c', "ReleaseLock syscall.\n");
			ReleaseLock_Syscall(machine->ReadRegister(4));
			break;
			case SC_DestroyLock:
			DEBUG('c', "DeleteLock syscall.\n");
			DeleteLock_Syscall(machine->ReadRegister(4));
			break;
			case SC_CreateCondition:
			DEBUG('c', "CreateCondition syscall.\n");
			rv = CreateCondition_Syscall(machine->ReadRegister(4),
				machine->ReadRegister(5));
			break;
			case SC_Wait:
			DEBUG('c', "ConditionWait.\n");
			ConditionWait_Syscall(machine->ReadRegister(4),
				machine->ReadRegister(5));
			break;
			case SC_Signal:
			DEBUG('c', "ConditionSignal.\n");
			ConditionSignal_Syscall(machine->ReadRegister(4),
				machine->ReadRegister(5));
			break;
			case SC_Broadcast:
			DEBUG('c', "ConditionBroadcast.\n");
			ConditionBroadcast_Syscall(machine->ReadRegister(4),
				machine->ReadRegister(5));
			break;
			case SC_DestroyCondition:
			DEBUG('c', "DeleteCondition.\n");
			DeleteCondition_Syscall(machine->ReadRegister(4));
			break;
			case SC_Rand:
			DEBUG('c', "Rand.\n");
			rv = rand() % machine->ReadRegister(4);
			break;
			case SC_CreateMonitor:
			#ifdef NETWORK
			DEBUG('c', "Create MV.\n");
			rv = CreateMonitor_Syscall(machine->ReadRegister(4),
				machine->ReadRegister(5), machine->ReadRegister(6));
			#endif
			break;
			case SC_DestroyMonitor:
			#ifdef NETWORK
			DestoryMonitor_Syscall(machine->ReadRegister(4));
			#endif
			break;
			case SC_ReadMonitor:
			#ifdef NETWORK
			rv = ReadMonitor_Syscall(machine->ReadRegister(4),
				machine->ReadRegister(5));
			#endif
			break;
			case SC_SetMonitor:
			#ifdef NETWORK
			SetMonitor_Syscall(machine->ReadRegister(4),
				machine->ReadRegister(5), machine->ReadRegister(6));
			#endif
			break;
		}

	// Put in the return value and increment the PC
		machine->WriteRegister(2,rv);
		machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
		machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
		machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
		return;
	} else if (which == PageFaultException) {
		//	handle TLB miss
		printf("TLB Miss.\n");
		interrupt->Halt();
/*		bool flag = false;
		int vpn = machine->ReadRegister(39) / PageSize;
		DEBUG('w', "VPN %d\n", machine->ReadRegister(39));
		int ppn = -1;
		DEBUG('v', "%d %d!!\n", currentThread->threadID, vpn);
		iptMutex->P();
		for(int i = 0; i < NumPhysPages; i++) {
			if (ipt[i].valid && ipt[i].process == currentThread->space && ipt[i].virtualPage == vpn && !ipt[i].change) {
				ppn = i;
				ipt[ppn].use = TRUE;
				break;
			}
		}
		iptMutex->V();
		
		DEBUG('v', "%d PPN %d!!\n", currentThread->threadID, ppn);
		if (ppn == -1) {
			// ipt miss
			ppn = handleIPTMiss(vpn);
		}
		IntStatus oldLevel = interrupt->SetLevel(IntOff);
		// set the TLB
		currentTLB = (currentTLB+1) % TLBSize;
		if (machine->tlb[currentTLB].valid) {
			ipt[machine->tlb[currentTLB].physicalPage].dirty = machine->tlb[currentTLB].dirty;
		}
		machine->tlb[currentTLB].virtualPage = ipt[ppn].virtualPage;
		machine->tlb[currentTLB].physicalPage = ppn;
		machine->tlb[currentTLB].valid = TRUE;
		machine->tlb[currentTLB].readOnly = ipt[ppn].readOnly;
		machine->tlb[currentTLB].use = FALSE;
		machine->tlb[currentTLB].dirty = ipt[ppn].dirty;
		interrupt->SetLevel(oldLevel);
		iptMutex->P();
		// clear the change and use bit
		ipt[ppn].change = FALSE;
		ipt[ppn].use = FALSE;
		iptMutex->V(); */
	} else {
		cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
		interrupt->Halt();
	}
}
